package com.hn.gege;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;
import com.baomidou.mybatisplus.generator.config.querys.OracleQuery;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.FileType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * 代码生成工具
 */
public class GegeGenerator {
    private static final Logger logger = LoggerFactory.getLogger(GegeGenerator.class);
    /** 项目模块名称 **/
    private static final String projectName = "gege-sys";
    /** 业务代码所在的根包名 **/
    private static final String modulesPack = "com.hn.gege.sys.modules";
    /** 公共模块包名（主要是类中依赖这个包里的一些内容） **/
    private static final String configPack = "com.hn.gege.core";
    public static void main(String[] args) {
        mysqlGenerator();
    }


    /**
     * mysql项目代码生成
     */
    public static void mysqlGenerator(){
        String type = "mysql";
        boolean isCreateAgain = false;
        String dcurl = "jdbc:mysql://localhost:3306/gege?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai";
        String dcname = "root";
        String dcpwd = "root";
        generator(type, projectName, modulesPack, configPack, isCreateAgain, dcurl, dcname, dcpwd);
    }

    /**
     * orcal项目代码生成
     */
    public static void oracleGenerator(){
        String type = "oracle";
        boolean isCreateAgain = true;
        String dcurl = "jdbc:oracle:thin:@localhost:1521:orcl";
        String dcname = "disa";
        String dcpwd = "disa";
        generator(type, projectName, modulesPack, configPack, isCreateAgain, dcurl, dcname, dcpwd);
    }

    /**
     * 代码生成
     * @param type 类别(mysql,oracle)
     * @param projectName 项目模块名(例: gege-sys)
     * @param modulesPack 业务代码所在的根包名(例: com.hn.gege.sys.modules)
     * @param configPack 公共模块包名（主要是类中依赖这个包里的一些内容）(例: com.hn.gege.core)
     * @param isCreateAgain 是否创建新文件替换旧文件
     * @param dcurl 数据库连接地址(例:"jdbc:mysql://localhost:3306/stu?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai")
     * @param dcname 数据库连接名称(例: root)
     * @param dcpwd  数据库连接密码(例: root)
     */
    public static void generator(String type, String projectName, final String modulesPack, final String configPack, final boolean isCreateAgain, String dcurl, String dcname, String dcpwd) {
        assert ("oracle".equals(type) || "mybatis".equals(type));
        //说明:具体配置查看--https://mp.baomidou.com/config/generator-config.html#%E6%95%B0%E6%8D%AE%E6%BA%90-datasourceconfig-%E9%85%8D%E7%BD%AE

        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 获取用户输入的表名,和业务包名
        String[] tableNames = scanner("表名，多个英文逗号分割").split(",");
        String modelName = scanner("业务包名");

        // 全局配置
        final GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir") + "/" + projectName;// 项目地址
        gc.setOutputDir(projectPath + "/src/main/java");// 文件输出的地址
        gc.setAuthor("zxy");// 作者
        gc.setOpen(false);// 是否打开输出文件位置
        gc.setSwagger2(true);// 是否启swagger2注解模式
//        gc.setIdType(IdType.INPUT);// id住建生成策略
        gc.setDateType(DateType.TIME_PACK);//设置时间类型为Date
        mpg.setGlobalConfig(gc);


        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl(dcurl);// 链接地址
        dsc.setUsername(dcname);// 用户名
        dsc.setPassword(dcpwd);// 密码
        if ("oracle".equals(type)) {
            dsc.setDriverName("oracle.jdbc.driver.OracleDriver");// 驱动名
            dsc.setDbQuery(new OracleQuery() {
                @Override
                public String[] fieldCustom() {//用于数据库的属性值, key表示主建
                    return new String[]{"KEY"};
                }
            });// 用于sql生成的判断
        } else if ("mysql".equals(type)) {
            dsc.setDriverName("com.mysql.cj.jdbc.Driver");// 驱动名
            dsc.setDbQuery(new MySqlQuery() {// null是数据库中用来判断是否为空的
                @Override
                public String[] fieldCustom() {//
                    return new String[]{"KEY", "NULL"};
                }
            });// 用于sql生成的判断
        }
        mpg.setDataSource(dsc);


        // 配置文件生成的模板
        TemplateConfig templateConfig = new TemplateConfig();
//        templateConfig.setEntity("templates/entity2.java");// 这里是原始模板所在的位置,在plus包里
        templateConfig.setController("/temp/" + type + "/myController.java");
        templateConfig.setEntity("/temp/" + type + "/myEntity.java");
        templateConfig.setService("/temp/" + type + "/myService.java");
        templateConfig.setServiceImpl("/temp/" + type + "/myServiceImpl.java");
//        templateConfig.setXml(null);// 如果不生成这个文件,将这个设置为null
        mpg.setTemplate(templateConfig);


        // 文件生成路劲配置
        final PackageConfig pc = new PackageConfig();
        pc.setParent(modulesPack);// 父包名
        pc.setModuleName(modelName);// 业务模块包名(生成的文件位置会是,父包名.业务模块包名这样)
        mpg.setPackageInfo(pc);


        // 自定义注入配置配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {// 自定义参数配置,模板引擎中可通过cfg.xxx引用
                Map<String, Object> map = new HashMap();
                map.put("configPack", configPack);// 配置公共模块的包名
                this.setMap(map);
            }
        };


        // 自定义文件生成
        /*ArrayList<CustomFileType> customFileTypes = new ArrayList<>();
        customFileTypes.add(CustomFileType.XML);
        customFileTypes.add(CustomFileType.VUE_APi);
        customFileTypes.add(CustomFileType.VUE_PAGE_LIST);
        customFileTypes.add(CustomFileType.VUE_EDIT);
        List<FileOutConfig> focList = customfile(customFileTypes, projectPath, modelName);//自定义生成文件列表
        cfg.setFileOutConfigList(focList);*/


        if (isCreateAgain) {// 判断是否需要覆盖
            // isCreate,用于判断是否需要创建文件
            // 默认逻辑是,如果文件不存在则创建,否则不创建
            // 这里可以重写逻辑,如果这里代码生效,就是永远会创建(也就是会覆盖)
            cfg.setFileCreate(new IFileCreate() {
                @Override
                public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                    return true;// 返回true表示生成文件,如果有则覆盖
                }
            });
        }
        // 文件覆盖策略,return true;表示覆盖
        mpg.setCfg(cfg);



        // 数据库策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setCapitalMode(false);// 数据库是否大写命名
        strategy.setSkipView(true);// 是否跳过视图
        strategy.setNaming(NamingStrategy.underline_to_camel);// 实体命名策略,转驼峰
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);// 字段命名策略,转驼峰
        strategy.setTablePrefix(modelName + "_");// 去除表名前缀(生成的实体去除前缀。如:前缀-ja,表名-ja_tt_xx,实体-ttXx)
        strategy.setChainModel(false);// 实体是否支持链式
        strategy.setEntityLombokModel(true);// 实体支持Lombok
        strategy.setRestControllerStyle(true);// controller是否带@RestController注解
        strategy.setControllerMappingHyphenStyle(true);// controller的Mapper是否以xx-xx的命名方式
        strategy.setInclude(tableNames);// 生成2文件的表名,通过上面用户输入得到
        strategy.setSuperControllerClass(configPack + ".common.BaseController");// Controller公共父类
//        strategy.setSuperEntityColumns("id");// 写于父类中的公共字段
        strategy.setLogicDeleteFieldName("DELETED");// 逻辑删除字段
        //设置自动填充字段
        ArrayList<TableFill> tableFills = new ArrayList();
        tableFills.add(new TableFill("ID", FieldFill.INSERT));
        tableFills.add(new TableFill("CREATE_DATE", FieldFill.INSERT));
        tableFills.add(new TableFill("MODIFY_DATE", FieldFill.UPDATE));
        strategy.setTableFillList(tableFills);
        // 需要生成文件的表,从上面用户输入得到
        strategy.setInclude(tableNames);
        mpg.setStrategy(strategy);

        // 设置模板引擎
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());

       // 执行
        mpg.execute();
    }

    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + "：");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotEmpty(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "！");
    }

    /**
     * 自定义文件类别
     */
    private enum CustomFileType {

        XML("myMapper.xml.ftl", "%sMapper.xml", "/src/main/resources/mapper/"),
        VUE_APi("vue-api.js.ftl","%s.js", "/src/main/resources/api/"),
        VUE_EDIT("vue-edit.vue.ftl","%sEdit.vue", "/src/main/resources/views/"),
        VUE_PAGE_LIST("vue-pageList.vue.ftl","%sPageInfo.vue", "/src/main/resources/views/");

        /**  文件模板名称 */
        private String templateName;
        /** 输出文件名称 */
        private String outFileName;
        /** 输出文件地址 */
        private String outFilePth;

        public String getTemplateName() {
            return templateName;
        }

        public void setTemplateName(String templateName) {
            this.templateName = templateName;
        }

        public String getOutFileName() {
            return outFileName;
        }

        public void setOutFileName(String outFileName) {
            this.outFileName = outFileName;
        }

        public String getOutFilePth() {
            return outFilePth;
        }

        public void setOutFilePth(String outFilePth) {
            this.outFilePth = outFilePth;
        }

        CustomFileType(String templateName, String outFileName, String outFilePth) {
            this.templateName = templateName;
            this.outFileName = outFileName;
            this.outFilePth = outFilePth;
        }
    }

    /**
     * 自定义文件生成
     * @param projectPath
     * @param modelName
     */
    private static List<FileOutConfig> customfile(List<CustomFileType> fileTypes, String projectPath, String modelName) {
        List<FileOutConfig> focList = new ArrayList();//自定义生成文件列表

        fileTypes.forEach(item -> {
            String templatePath = "/temp/" + item.getTemplateName();// 文件模板地址
            String outputFilePath = projectPath + item.getOutFilePth();// 生成文件地址
            String finalOutFileName = item.getOutFileName();// 文件生成名称
            FileOutConfig fileOutConfig = new FileOutConfig(templatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 返回完整文件路劲
                    return outputFilePath + lowerFirstCapse(String.format(finalOutFileName, tableInfo.getEntityName()));
                }
            };
            focList.add(fileOutConfig);
        });
        return focList;
    }

    /**
     * 首字母小写
     * @param str
     * @return
     */
    public static String lowerFirstCapse(String str){
        if (str.indexOf(".js") > -1 || str.indexOf(".vue") > -1) {
            char[]chars = str.toCharArray();
            chars[0] += 32;
            return String.valueOf(chars);
        }
        return str;
    }
}
